package com.deneme;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.UUID;

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.memcache.MemcacheService;
import com.google.appengine.api.memcache.MemcacheServiceException;
import com.google.appengine.api.memcache.MemcacheServiceFactory;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;

import functions.images.PuzzleDifficulty;

@SuppressWarnings("serial")
public class SavedPuzzle implements Serializable {
	private UUID id; // id of puzzle
	private BlobKey imgKey; // image used for the puzzle
	private int[] config; // location of the pieces
	private int dimension; // dimensions of puzzle
	private PuzzleDifficulty difficulty; // how many shuffles
	private ArrayList<String> contributors; // who's contributed to this puzzle
	private Date saveDate; // when was the puzzle saved?
	
	public SavedPuzzle() {
		id = UUID.randomUUID();
		contributors = new ArrayList<String>();
	}
	
	public SavedPuzzle(BlobKey key, int[] c, int dim, PuzzleDifficulty diff) {
		id = UUID.randomUUID();
		imgKey = key;
		config = c;
		dimension = dim;
		difficulty = diff;
		contributors = new ArrayList<String>();
	}

	public ArrayList<String> getContributors() {
		return contributors;
	}

	/* convert contributors from arraylist to string representation */
	public String getContributorsString() {
		StringBuilder sb = new StringBuilder();
		for(String str : contributors) {
			sb.append(str + ", ");
		}
		sb.delete(sb.lastIndexOf(","), sb.length());
		return sb.toString();
	}

	public Date getSaveDate() {
		return saveDate;
	}

	public void setContributors(ArrayList<String> contributors) {
		this.contributors = contributors;
	}

	public void setSaveDate(Date saveDate) {
		this.saveDate = saveDate;
	}

	public void setId(String id) {
		this.id = UUID.fromString(id);
	}

	public String getId() {
		return id.toString();
	}

	public BlobKey getImgKey() {
		return imgKey;
	}

	public void setImgKey(BlobKey imgKey) {
		this.imgKey = imgKey;
	}

	public int[] getConfig() {
		return config;
	}

	public void setConfig(int[] config) {
		this.config = config;
	}

	public int getDimension() {
		return dimension;
	}

	public void setDimension(int dimension) {
		this.dimension = dimension;
	}

	public PuzzleDifficulty getDifficulty() {
		return difficulty;
	}

	public void setDifficulty(PuzzleDifficulty difficulty) {
		this.difficulty = difficulty;
	}
	
	/* calculate how complete the puzzle is based on how
	 * many tiles are in the correct location */
	public double getPercentComplete() {
		if(config == null) {
			return 0.0;
		}
		int i;
		int count = 0;
		for(i = 0; i < config.length; ++i) {
			if(config[i] == i){
				++count;
			}
		}
		double toReturn = ((double)count/i)*100;
		toReturn = ((double)((int) (toReturn * 100))/100);
		return toReturn;
	}
	
	@SuppressWarnings("unchecked")
	public static SavedPuzzle getPuzzleForKey(String key) {
		Key datastoreKey = KeyFactory.createKey("SavedPuzzles", key);
		Entity puzzleEntity = null;
		String cacheKey = "SavedPuzzles:" + key;
		try {
			MemcacheService memcache = MemcacheServiceFactory.getMemcacheService();
			if (memcache.contains(cacheKey)) {
				System.out.println("CACHE HIT");
				puzzleEntity = (Entity) memcache.get(cacheKey);
				return entityToPuzzle(puzzleEntity);
			}
			System.out.println("CACHE MISS");
		} catch (MemcacheServiceException e) {
			// If there is a problem with the cache,
			// fall through to the datastore.
		}
		DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
		SavedPuzzle savedPuzzle = null;
		try {
			Query query = new Query("SavedPuzzles", datastoreKey);
			puzzleEntity = datastore.prepare(query).asSingleEntity();
			if(puzzleEntity != null) {
				try {
					MemcacheService memcache = MemcacheServiceFactory.getMemcacheService();
					memcache.put(cacheKey, puzzleEntity);
				} catch (MemcacheServiceException e) {
					// Ignore cache problems, nothing we can do.
				}
				savedPuzzle = entityToPuzzle(puzzleEntity);
			} else {
				return null;
			}
		} finally {
		}
		System.out.println("Hitting the final return");
		return savedPuzzle;
	}

	/* this deletes the puzzle from the memcache and datastore */
	public void delete() {
		/* get appropriate storage services and generate keys */
		DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
		Key datakey = KeyFactory.createKey("SavedPuzzles", id.toString());

		String cacheKey = "SavedPuzzles:" + id.toString();
		MemcacheService memcache = MemcacheServiceFactory.getMemcacheService();
		
		/* remove from storage */
		if (memcache.contains(cacheKey)) {
			memcache.delete(cacheKey);
		}
		
		datastore.delete(datakey);
	}
	
	/* this saves a puzzle to the memcache and datastore */
	public void save() {
		/* get appropriate storage services and generate keys */
		DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
		Key datakey = KeyFactory.createKey("SavedPuzzles", id.toString());

		String cacheKey = "SavedPuzzles:" + id.toString();
		MemcacheService memcache = MemcacheServiceFactory.getMemcacheService();

		/* look for puzzle in memcache */
		Entity puzzle = null;
		if (memcache.contains(cacheKey)) {
			puzzle = (Entity) memcache.get(cacheKey);
		}
		/* if it's not there, look for it in datastore */
		if (puzzle == null) {
			Query query = new Query("SavedPuzzles", datakey);
			puzzle = datastore.prepare(query).asSingleEntity();
		}
		/* if not in datastore, create new puzzle */
		if(puzzle == null) {
			puzzle = new Entity("SavedPuzzles", datakey);
		}
		
		/* convert configuration to string */
		String configStr;
		if(this.config != null) {
			StringBuilder sb = new StringBuilder();
			for (int i : this.config) {
				sb.append(i + ":");
			}
			configStr = sb.toString();
		}
		else {
			configStr = "";
		}
		
		/* get current user information and add them to the contributors */
		UserService userService = UserServiceFactory.getUserService();
		User user = userService.getCurrentUser();
		String username = user.getNickname();
		if (!contributors.contains(username)) {
			contributors.add(username);
		}
		
		/* synch up puzzle information with entity */
		puzzle.setProperty("id", this.id.toString());
		puzzle.setProperty("configstring", configStr);
		puzzle.setProperty("difficulty", this.difficulty.toString());
		puzzle.setProperty("dimension", this.dimension);
		puzzle.setProperty("blobkey", this.imgKey);
		puzzle.setProperty("contributors", this.contributors);
		puzzle.setProperty("savedate", new Date().getTime());
		
		/* save the puzzle */
		datastore.put(puzzle);
		
		try {
			memcache.put(cacheKey, puzzle);
		} catch (MemcacheServiceException e) {
			// Ignore cache problems, nothing we can do.
		}
	}

	/* converts an entity to a puzzle object */
	public static SavedPuzzle entityToPuzzle(Entity entity) {
		SavedPuzzle puzzle = new SavedPuzzle();
		/* convert config from string to int array */
		String configString = (String)entity.getProperty("configstring");
		String[] configStringArr = configString.split(":");
		int[] config = new int[configStringArr.length];

		if (configString.isEmpty()) {
			config = null;
		}
		else {
			for (int i = 0; i < configStringArr.length; ++i) {
				config[i] = Integer.parseInt(configStringArr[i]);
			}
		}
		
		/* retreive all other data from the entity */
		puzzle.setId((String)entity.getProperty("id"));
		puzzle.setConfig(config);
		puzzle.setDifficulty(PuzzleDifficulty.valueOf((String)entity.getProperty("difficulty")));
		puzzle.setDimension((Integer)entity.getProperty("dimension"));
		puzzle.setImgKey((BlobKey)entity.getProperty("blobkey"));
		puzzle.setContributors((ArrayList<String>)entity.getProperty("contributors"));
		puzzle.setSaveDate(new Date((Long)entity.getProperty("savedate")));
		return puzzle;
	}

}
